﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GDoc.Data;
using AutoMapper;
using GDoc.Domain;
using Microsoft.WindowsAzure.StorageClient;
using System.Data.Services.Client;

namespace GDoc.Data.Azure
{
    public class FileSystemEntriesRepository : IFileSystemEntriesRepository
    {
        /// <summary>
        /// The set to interact with the storage.
        /// </summary>
        public virtual StorageSet<FileSystemEntryAzure> Set { get; set; }

        /// <summary>
        /// UnitOfWork to be used in the derived repositiores.
        /// </summary>
        public virtual UnitOfWork UnitOfWork { get; set; }

        static FileSystemEntriesRepository()
        {
            Mapper.CreateMap<FileSystemEntry, FileSystemEntryAzure>()
                .ForMember(dest => dest.PartitionKey, opt => opt.MapFrom(src => String.Format("urn:account:{0}", src.AccountId)))
                .ForMember(dest => dest.RowKey, opt => opt.MapFrom(src => String.Format("urn:fullpath:{0}", src.FullPath.Replace('/', '|'))));
            Mapper.CreateMap<FileSystemEntryAzure, FileSystemEntry>();
        }

        public FileSystemEntriesRepository(IUnitOfWork unitOfWorkInterface)
        {
            UnitOfWork = unitOfWorkInterface as UnitOfWork;
            Set = UnitOfWork.Context.FileSystem;
        }

        public IEnumerable<FileSystemEntry> Browse(long accountId, string path)
        {
            var pk = String.Format("urn:account:{0}", accountId);

            var results = Set.Data.Where
            (s =>
                s.PartitionKey == pk &&
                s.Path == path
            ).ToList();

            return Mapper.Map<IEnumerable<FileSystemEntryAzure>, IEnumerable<FileSystemEntry>>(results);
        }

        public void Add(FileSystemEntry entry)
        {
            SaveOrUpdate(entry);
        }

        public void Update(FileSystemEntry entry)
        {
            SaveOrUpdate(entry);
        }

        public void Remove(FileSystemEntry entry)
        {
            string pk;
            string rk;
            BuildKeys(entry, out pk, out rk);

            var entryAzure = Get(pk, rk);

            if (entryAzure != null)
            {
                Set.Delete(entryAzure);
                Set.SaveChanges();
            }
        }

        public FileSystemEntry Get(long accountId, string fullPath)
        {
            string pk;
            string rk;

            BuildKeys(accountId, fullPath, out pk, out rk);

            var entry = Get(pk, rk);

            if (entry != null)
                return Mapper.Map<FileSystemEntryAzure, FileSystemEntry>(entry);
            else
                return null;
        }

        private FileSystemEntryAzure Get(string pk, string rk)
        {
            try
            {
                var result = Set.Data.Where
                (s =>
                    s.PartitionKey == pk &&
                    s.RowKey == rk
                ).FirstOrDefault();

                return result;
            }
            catch (DataServiceQueryException ex)
            {
                // if (ex.Response.StatusCode == 404)
                return null;
            }
        }

        private void SaveOrUpdate(FileSystemEntry entry)
        {
            string pk;
            string rk;
            BuildKeys(entry, out pk, out rk);

            FileSystemEntryAzure entryAzure = Get(pk, rk);

            if (entryAzure == null)
            {
                entryAzure = Mapper.Map<FileSystemEntry, FileSystemEntryAzure>(entry);
                Set.Add(entryAzure);
                Set.SaveChanges();
            }
            else
            {
                entryAzure = Mapper.Map<FileSystemEntry, FileSystemEntryAzure>(entry, entryAzure);
                Set.Update(entryAzure);
                Set.SaveChanges();
            }
        }

        private static void BuildKeys(FileSystemEntry entry, out string pk, out string rk)
        {
            BuildKeys(entry.AccountId, entry.FullPath, out pk, out rk);
        }

        private static void BuildKeys(long accountId, string fullPath, out string pk, out string rk)
        {
            pk = String.Format("urn:account:{0}", accountId);
            rk = String.Format("urn:fullpath:{0}", fullPath.Replace('/', '|'));
        }
    }
}
